// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once

#include <exception>
#include <stdexcept>

#include <ChakraDebugProtocolHandler.h>
#include <ChakraDebugService.h>


class DebugProtocolHandler
{
private:
  JsDebugProtocolHandler m_protocolHandler{ nullptr };

public:
  DebugProtocolHandler(JsRuntimeHandle runtime)
  {
    JsDebugProtocolHandler protocolHandler;

    JsErrorCode result = JsDebugProtocolHandlerCreate(runtime, &protocolHandler);

    if (result != JsNoError)
    {
      throw new std::runtime_error("Unable to create debug protocol handler.");
    }

    m_protocolHandler = protocolHandler;
  }

  ~DebugProtocolHandler()
  {
    Destroy();
  }

  JsErrorCode Connect(bool breakOnNextLine, JsDebugProtocolHandlerSendResponseCallback callback, void* callbackState)
  {
    JsErrorCode result = JsDebugProtocolHandlerConnect(m_protocolHandler,
      breakOnNextLine, callback, callbackState);

    return result;
  }

  JsErrorCode Destroy()
  {
    JsErrorCode result = JsNoError;

    if (m_protocolHandler != nullptr)
    {
      result = JsDebugProtocolHandlerDestroy(m_protocolHandler);

      if (result == JsNoError)
      {
        m_protocolHandler = nullptr;
      }
    }

    return result;
  }

  JsErrorCode Disconnect()
  {
    JsErrorCode result = JsDebugProtocolHandlerDisconnect(m_protocolHandler);

    return result;
  }

  JsDebugProtocolHandler GetHandle()
  {
    return m_protocolHandler;
  }

  JsErrorCode ProcessCommandQueue()
  {
    JsErrorCode result = JsDebugProtocolHandlerProcessCommandQueue(m_protocolHandler);

    return result;
  }

  JsErrorCode SetCommandQueueCallback(JsDebugProtocolHandlerCommandQueueCallback callback, void* callbackState)
  {
    JsErrorCode result = JsDebugProtocolHandlerSetCommandQueueCallback(m_protocolHandler, callback, callbackState);

    return result;
  }

  JsErrorCode WaitForDebugger()
  {
    JsErrorCode result = JsDebugProtocolHandlerWaitForDebugger(m_protocolHandler);

    return result;
  }

  JsErrorCode GetConsoleObject(JsValueRef* consoleObject)
  {
    JsErrorCode result = JsDebugProtocolHandlerCreateConsoleObject(m_protocolHandler, consoleObject);

    return result;
  }
};


class DebugService
{
private:
  JsDebugService m_service{ nullptr };

public:
  DebugService(JsRuntimeHandle runtime)
  {
    JsDebugService service;

    JsErrorCode result = JsDebugServiceCreate(&service);

    if (result != JsNoError)
    {
      throw new std::exception("Unable to create debug service.");
    }

    m_service = service;
  }

  ~DebugService()
  {
    Destroy();
  }

  JsErrorCode Close()
  {
    JsErrorCode result = JsDebugServiceClose(m_service);

    return result;
  }

  JsErrorCode Destroy()
  {
    JsErrorCode result = JsNoError;

    if (m_service != nullptr)
    {
      result = JsDebugServiceDestroy(m_service);

      if (result == JsNoError)
      {
        m_service = nullptr;
      }
    }

    return result;
  }

  JsErrorCode Listen(uint16_t port)
  {
    JsErrorCode result = JsDebugServiceListen(m_service, port);

    return result;
  }

  JsErrorCode RegisterHandler(std::string const& runtimeName, DebugProtocolHandler& protocolHandler, bool breakOnNextLine)
  {
    JsErrorCode result = JsDebugServiceRegisterHandler(m_service, runtimeName.c_str(), protocolHandler.GetHandle(), breakOnNextLine);

    return result;
  }

  JsErrorCode UnregisterHandler(std::string const& runtimeName)
  {
    JsErrorCode result = JsDebugServiceUnregisterHandler(m_service, runtimeName.c_str());

    return result;
  }
};
